package org.lxg.spring.pojo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class Broker {

    @Before("execution(* org.lxg.spring.pojo.Landlord.service())")
    public void before() {
        System.out.println("带租客看房——开始");
        System.out.println("谈价格—开始");
    }

    @After("execution(* org.lxg.spring.pojo.Landlord.service())")
    public void after() {
        System.out.println("交钥匙—最终");
    }

    //  使用 @Around 注解来同时完成前置和后置通知
    @Around("execution(* org.lxg.spring.pojo.Landlord.service())")
    public void around(ProceedingJoinPoint joinPoint) {
        System.out.println("带租客看房");
        System.out.println("谈价格");

        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

        System.out.println("交钥匙");
    }

    /* 定义需要织入的方法， 可以多个使用避免写多个 */
    @Pointcut("execution(* org.lxg.spring.pojo.Landlord.service())")
    public void lService() {
    }

    @AfterReturning("lService()")
    public void afterReturn() {
        /**
         * 返回通知，在连接点方法执行并正常返回后调用，要求连接点方法在执行过程中没有发生异常
         * 发生异常时也会执行
         */
        System.out.println("交易完成");
    }

    @Pointcut("execution(* org.lxg.spring.pojo.Landlord.serviceForMoney())")
    public void serviceForMoney() {
    }

    @AfterThrowing("serviceForMoney()")
    public void afterThrow() {
        /**
         * 异常通知，当连接点方法异常时调用, 没有异常时不会执行
         */
        System.out.println("交易异常");
    }

    /**
     *
     * execution：代表执行方法的时候会触发
     * * ：代表任意返回类型的方法
     * pojo.Landlord：代表类的全限定名
     * service()：被拦截的方法名称
     *  执行顺序：  before() --> around() --> after() --> afterReturnning() --> afterTrowing()
     *  如果有两个 Aspect 此时需要制定 执行顺序：
     *    实现制定顺序方式：
     *      实现org.springframework.core.Ordered接口，实现它的getOrder()方法
     *      给aspect添加@Order注解，该注解全称为：org.springframework.core.annotation.Order
     * @Order(1)
     * @Order(2)
     *      则 上述的执行方式是： 1_befor() -->  2_befor()--> 2_around() .... --> 2_afterThrowing()--> 1_around().....--> 1_afterThrowing();
     *
     *     对于 @Around 方法 不管它有没有返回值，但是必须要方法内部，调用一下 pjp.proceed();否则，Controller 中的接口将没有机会被执行，从而也导致了 @Before这个advice不会被触发。
     */
}